import os
import subprocess
import unittest
import sys
import time
import logging
from typing import List, Dict, Any, Optional, Tuple

class Colors:
    """颜色输出类"""
    RED = '\033[91m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    BLUE = '\033[94m'
    MAGENTA = '\033[95m'
    CYAN = '\033[96m'
    WHITE = '\033[97m'
    RESET = '\033[0m'
    BOLD = '\033[1m'

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(sys.stdout),
        logging.FileHandler('test_keels_cli.log')
    ]
)
logger = logging.getLogger(__name__)

# 全局设备ID配置
DEVICE_IDS = {
    "android_device": os.environ.get("ANDROID_DEVICE_ID", ""),
    "android_emulator": os.environ.get("ANDROID_EMULATOR_ID", ""),
    "harmony_device": os.environ.get("HARMONY_DEVICE_ID", ""),
    "harmony_emulator": os.environ.get("HARMONY_EMULATOR_ID", ""),
    "ios_device": os.environ.get("IOS_DEVICE_ID", ""),
    "ios_simulator": os.environ.get("IOS_SIMULATOR_ID", "")
}

class TestKeelsCLI(unittest.TestCase):
    def setUp(self):
        """测试前准备"""
        self.original_cwd = os.getcwd()
        self.test_counter = 0
        self.current_test_dir = None
        logger.info(f"当前测试目录: {self.original_cwd}")

    def tearDown(self):
        """测试后清理"""
        # 切换回原目录
        os.chdir(self.original_cwd)
        logger.info("测试完成，已切换回原目录")

    def run_command(self, command, check=True, cwd=None):
        """运行命令并返回结果"""
        try:
            current_dir = cwd or os.getcwd()
            logger.info(f"执行命令: {command}")
            logger.info(f"当前目录: {current_dir}")

            result = subprocess.run(
                command,
                shell=True,
                capture_output=True,
                text=True,
                cwd=cwd
            )

            # 根据返回码记录不同级别的日志
            if result.returncode == 0:
                logger.info(f"命令返回值: {result.returncode}")
            else:
                logger.error(f"命令返回值: {result.returncode}")

            # 记录所有输出
            all_output = result.stdout + result.stderr
            if all_output.strip():
                logger.info(f"命令输出: {all_output}")

            if check and result.returncode != 0:
                error_msg = f"命令失败: {command}\n返回码: {result.returncode}\n输出: {all_output}"
                logger.error(error_msg)
                self.fail(error_msg)

            return result

        except Exception as e:
            error_msg = f"命令执行异常: {command}\n异常: {str(e)}"
            logger.error(error_msg)
            if check:
                self.fail(error_msg)
            return None

    def create_project(self, project_type: str, project_name: str) -> bool:
        """创建项目"""
        logger.info(f"开始创建项目: {project_type} - {project_name}")

        # 根据项目类型确定创建命令
        if project_type == "app":
            create_cmd = f"keels create --app {project_name}"
        elif project_type.startswith("package"):
            create_cmd = f"keels create --package {project_name}"
        elif project_type == "module":
            create_cmd = f"keels create --module {project_name}"
        else:
            logger.warning(f"未知的项目类型: {project_type}")
            return False

        # 创建项目
        result = self.run_command(create_cmd, check=False)
        if result.returncode != 0:
            logger.warning(f"创建{project_type}失败")
            return False

        if not os.path.exists(project_name):
            logger.warning(f"项目目录未创建: {project_name}")
            return False

        logger.info(f"项目创建成功: {project_name}")
        self.current_test_dir = os.path.join(self.original_cwd, project_name)
        return True

    def build_project(self, product: str, compile_type: str, project_dir: str = None) -> bool:
        """构建项目"""
        logger.info(f"开始构建项目: {product} - {compile_type}")

        build_dir = project_dir or self.current_test_dir or self.original_cwd
        build_cmd = f"keels build {product} --{compile_type}"

        result = self.run_command(build_cmd, check=False, cwd=build_dir)
        build_success = result.returncode == 0

        if build_success:
            logger.info(f"构建成功: {product} - {compile_type}")
        else:
            logger.warning(f"构建失败: {product} - {compile_type}")

        return build_success

    def run_project(self, product: str, compile_type: str, project_dir: str = None) -> bool:
        """运行项目"""
        logger.info(f"开始运行项目: {product} - {compile_type}")

        run_dir = project_dir or self.current_test_dir or self.original_cwd

        # 根据产物类型确定设备ID
        device_id = ""
        if product in[ "apk", "aar"]:
            device_id = DEVICE_IDS["android_device"] or DEVICE_IDS["android_emulator"]
        elif product == ["hap", "har"]:
            device_id = DEVICE_IDS["harmony_device"] or DEVICE_IDS["harmony_emulator"]
        elif product in ["ios", "ipa", "framework"]:
            device_id = DEVICE_IDS["ios_device"]
        elif product in ["ios-sim", "framework-sim"]:
            device_id = DEVICE_IDS["ios_simulator"]

        # 构建运行命令
        if device_id:
            run_cmd = f"keels run --{compile_type} -d {device_id}"
        else:
            run_cmd = f"keels run --{compile_type}"

        result = self.run_command(run_cmd, check=False, cwd=run_dir)
        run_success = result.returncode == 0

        if run_success:
            logger.info(f"运行成功: {product} - {compile_type}")
        else:
            logger.warning(f"运行失败: {product} - {compile_type}")

        return run_success

    def verify_build_output(self, platform: str, target_platform: str,
                          product: str, compile_type: str, project_dir: str) -> bool:
        """验证构建输出"""
        logger.info(f"验证构建输出: {platform} - {target_platform} - {product}")

        build_dir = project_dir or self.current_test_dir or self.original_cwd
        verification_passed = True

        if platform == "MacOS" and target_platform == "iOS":
            # 根据产物类型确定构建目录
            if product in ["ios", "framework", "ipa"]:
                sdk_type = "iphoneos"  # 真机构建
            elif product in ["ios-sim", "framework-sim"]:
                sdk_type = "iphonesimulator"  # 模拟器构建
            else:
                sdk_type = "unknown"

            if sdk_type != "unknown":
                build_output_dir = os.path.join("ios", "output", "DerivedData", "Build", "Products",
                                              f"{compile_type.capitalize()}-{sdk_type}")

                if os.path.exists(os.path.join(build_dir, build_output_dir)):
                    logger.info(f"iOS构建目录存在: {build_output_dir}")
                    # 检查是否有实际构建产物
                    build_files = os.listdir(os.path.join(build_dir, build_output_dir))
                    if build_files:
                        logger.info(f"iOS构建产物: {build_files}")
                    else:
                        logger.warning(f"iOS构建目录为空: {build_output_dir}")
                        verification_passed = False
                else:
                    logger.warning(f"iOS构建目录不存在: {build_output_dir}")
                    verification_passed = False

        elif target_platform == "Android":
            if product == "apk":
                # Android APK构建验证
                apk_dir = os.path.join("android", "app", "build", "outputs", "apk", compile_type)
                if os.path.exists(os.path.join(build_dir, apk_dir)):
                    logger.info(f"APK目录存在: {apk_dir}")
                    # 检查是否有APK文件
                    apk_files = [f for f in os.listdir(os.path.join(build_dir, apk_dir)) if f.endswith('.apk')]
                    if apk_files:
                        logger.info(f"找到APK文件: {apk_files}")
                    else:
                        logger.warning(f"APK目录中没有APK文件: {apk_dir}")
                        verification_passed = False
                else:
                    logger.warning(f"APK目录不存在: {apk_dir}")
                    verification_passed = False

            elif product == "aar":
                # Android AAR构建验证
                aar_dir = os.path.join("android", "build", "outputs", "aar")
                if os.path.exists(os.path.join(build_dir, aar_dir)):
                    logger.info(f"AAR目录存在: {aar_dir}")
                    # 检查是否有AAR文件
                    aar_files = [f for f in os.listdir(os.path.join(build_dir, aar_dir)) if f.endswith('.aar')]
                    if aar_files:
                        logger.info(f"找到AAR文件: {aar_files}")
                    else:
                        logger.warning(f"AAR目录中没有AAR文件: {aar_dir}")
                        verification_passed = False
                else:
                    logger.warning(f"AAR目录不存在: {aar_dir}")
                    verification_passed = False

        elif target_platform == "OHOS":
            if product == "hap":
                # OHOS HAP构建验证
                hap_dir = os.path.join("build", "outputs", "hap", compile_type)
                if os.path.exists(os.path.join(build_dir, hap_dir)):
                    logger.info(f"HAP目录存在: {hap_dir}")
                    # 检查是否有HAP文件
                    hap_files = [f for f in os.listdir(os.path.join(build_dir, hap_dir)) if f.endswith('.hap')]
                    if hap_files:
                        logger.info(f"找到HAP文件: {hap_files}")
                    else:
                        logger.warning(f"HAP目录中没有HAP文件: {hap_dir}")
                        verification_passed = False
                else:
                    logger.warning(f"HAP目录不存在: {hap_dir}")
                    verification_passed = False

            elif product == "har":
                # OHOS HAR构建验证
                har_dir = os.path.join("build", "outputs", "har")
                if os.path.exists(os.path.join(build_dir, har_dir)):
                    logger.info(f"HAR目录存在: {har_dir}")
                    # 检查是否有HAR文件
                    har_files = [f for f in os.listdir(os.path.join(build_dir, har_dir)) if f.endswith('.har')]
                    if har_files:
                        logger.info(f"找到HAR文件: {har_files}")
                    else:
                        logger.warning(f"HAR目录中没有HAR文件: {har_dir}")
                        verification_passed = False
                else:
                    logger.warning(f"HAR目录不存在: {har_dir}")
                    verification_passed = False

        return verification_passed

    def execute_test_combination(self, combination: Dict[str, Any]):
        """根据参数组合执行测试"""
        self.test_counter += 1
        test_number = self.test_counter

        platform = combination['platform']
        project_type = combination['project_type']
        target_platform = combination['target_platform']
        compile_type = combination['compile_type']
        product = combination['product']

        test_name = f"{platform}_{project_type}_{target_platform}_{product}_{compile_type}"
        test_name = test_name.replace("-", "_").lower()

        logger.info(f"=== 开始测试 {test_number}: {platform} {project_type} {target_platform} {compile_type} {product} ===")

        # 1. 创建项目
        if not self.create_project(project_type, test_name):
            self.skipTest(f"创建{project_type}失败")
            return

        # 2. 构建项目
        build_success = self.build_project(product, compile_type)

        # 3. 验证构建输出
        verification_passed = self.verify_build_output(platform, target_platform, product, compile_type, self.current_test_dir)

        # 4. 尝试运行（如果适用）
        run_success = False
        if product in ["apk", "ios", "ios-sim", "hap"] and build_success:
            run_success = self.run_project(product, compile_type)

        # 记录测试结果
        if build_success and verification_passed:
            logger.info(f"测试 {test_number} 成功完成")
        else:
            logger.warning(f"测试 {test_number} 失败 - 构建: {build_success}, 验证: {verification_passed}")
            self.fail(f"测试失败 - 构建: {build_success}, 验证: {verification_passed}")

# 其余 ParameterDictionary 类和辅助函数保持不变
class ParameterDictionary:
    # ... (保持原有的 ParameterDictionary 类不变)
    # 平台定义及其支持的目标平台
    PLATFORMS = {
        "Windows": ["Android", "OHOS"],
        "MacOS": ["iOS", "Android", "OHOS"]
    }

    # 项目类型及其可输出的产物类型
    PROJECT_TYPE = {
        "app": ["apk", "ios", "ios-sim", "hap", "ipa"],
        "package-keels": ["aar", "har", "framework", "framework-sim"],
        "package-native": ["aar", "har", "framework", "framework-sim"],
        "module": ["aar", "har", "framework", "framework-sim"]
    }

    # 目标平台及其支持的产物类型
    TARGET_PLATFORMS = {
        "Android": ["apk", "aar"],
        "iOS": ["ios", "ipa", "framework", "ios-sim", "framework-sim"],
        "OHOS": ["hap", "har"]
    }

    # 编译类型
    COMPILE_TYPE = ["debug", "release"]

    @classmethod
    def generate_combinations(
        cls,
        platforms: Optional[List[str]] = None,
        target_platforms: Optional[List[str]] = None
    ) -> List[Dict[str, Any]]:
        """
        根据传入的平台和目标平台参数生成有效的参数组合

        Args:
            platforms: 指定的平台列表，如果为None则使用所有平台
            target_platforms: 指定的目标平台列表，如果为None则使用所有目标平台

        Returns:
            List[Dict]: 包含有效参数组合的字典列表
        """
        # 验证参数顺序：必须先platforms后target_platforms
        if target_platforms is not None and platforms is None:
            raise ValueError("必须先指定platforms参数，然后才能指定target_platforms参数")

        # 处理平台参数
        if platforms is None:
            selected_platforms = list(cls.PLATFORMS.keys())
        else:
            # 验证传入的平台是否有效
            invalid_platforms = [p for p in platforms if p not in cls.PLATFORMS]
            if invalid_platforms:
                raise ValueError(f"无效的平台参数: {invalid_platforms}")
            selected_platforms = platforms

        # 处理目标平台参数
        if target_platforms is None:
            # 获取选定平台支持的所有目标平台
            all_targets = set()
            for platform in selected_platforms:
                all_targets.update(cls.PLATFORMS[platform])
            selected_target_platforms = list(all_targets)
        else:
            # 验证传入的目标平台是否有效
            all_valid_targets = set()
            for platform in selected_platforms:
                all_valid_targets.update(cls.PLATFORMS[platform])

            invalid_targets = [t for t in target_platforms if t not in all_valid_targets]
            if invalid_targets:
                raise ValueError(f"无效的目标平台参数: {invalid_targets}")
            selected_target_platforms = target_platforms

        # 生成所有有效的组合
        combinations = []
        for platform in selected_platforms:
            for target_platform in selected_target_platforms:
                # 检查该平台是否支持该目标平台
                if target_platform in cls.PLATFORMS[platform]:
                    # 获取该项目类型和目标平台支持的产物类型交集
                    for project_type in cls.PROJECT_TYPE.keys():
                        for compile_type in cls.COMPILE_TYPE:
                            # 获取项目类型和目标平台都支持的产物
                            valid_products = set(cls.PROJECT_TYPE[project_type]) & set(cls.TARGET_PLATFORMS[target_platform])

                            for product in valid_products:
                                combination = {
                                    "platform": platform,
                                    "project_type": project_type,
                                    "target_platform": target_platform,
                                    "compile_type": compile_type,
                                    "product": product
                                }
                                combinations.append(combination)

        return combinations

    @classmethod
    def print_combinations(
        cls,
        platforms: Optional[List[str]] = None,
        target_platforms: Optional[List[str]] = None
    ) -> None:
        """
        打印生成的参数组合（用于调试和查看结果）
        """
        combinations = cls.generate_combinations(platforms, target_platforms)

        print(f"生成的组合数量: {len(combinations)}")
        print("=" * 80)

        for i, combo in enumerate(combinations, 1):
            print(f"{i:3d}. Platform: {combo['platform']:8} | "
                  f"Project: {combo['project_type']:15} | "
                  f"Target: {combo['target_platform']:8} | "
                  f"Product: {combo['product']} | "
                  f"Compile: {combo['compile_type']:8}")

    @classmethod
    def get_filtered_combinations(
        cls,
        platforms: Optional[List[str]] = None,
        target_platforms: Optional[List[str]] = None,
        project_types: Optional[List[str]] = None,
        compile_types: Optional[List[str]] = None,
        products: Optional[List[str]] = None
    ) -> List[Dict[str, Any]]:
        """
        生成组合并进行进一步筛选

        Args:
            platforms: 指定的平台列表
            target_platforms: 指定的目标平台列表
            project_types: 指定的项目类型列表
            compile_types: 指定的编译类型列表
            products: 指定的产物类型列表

        Returns:
            List[Dict]: 筛选后的参数组合列表
        """
        combinations = cls.generate_combinations(platforms, target_platforms)

        # 应用额外的筛选条件
        filtered_combinations = []
        for combo in combinations:
            if project_types and combo['project_type'] not in project_types:
                continue
            if compile_types and combo['compile_type'] not in compile_types:
                continue
            if products and combo['product'] not in products:
                continue
            filtered_combinations.append(combo)

        return filtered_combinations

def create_dynamic_test_method(combo):
    """为每个组合创建动态测试方法"""
    def test_method(self):
        return self.execute_test_combination(combo)
    return test_method

def create_dynamic_tests():
    """创建动态测试方法"""
    # 生成测试组合（这里可以根据需要传入特定参数）
    combinations = ParameterDictionary.get_filtered_combinations(
        platforms=["MacOS"],
        target_platforms=["iOS"],
        project_types=["app", "package-keels", "module"],
        compile_types=["debug"],
        products=["ios", "ios-sim"]
    )

    # 为每个组合创建测试方法
    for i, combination in enumerate(combinations, 1):
        # 设置方法名
        platform = combination['platform']
        project_type = combination['project_type']
        target_platform = combination['target_platform']
        compile_type = combination['compile_type']
        product = combination['product']

        test_name = f"{platform}_{project_type}_{target_platform}_{product}_{compile_type}"
        test_name = test_name.lower()
        method_name = f"test_{i:03d}_{test_name}"

        # 创建测试方法并动态添加到测试类
        test_method = create_dynamic_test_method(combination)
        setattr(TestKeelsCLI, method_name, test_method)

if __name__ == "__main__":
    # 打印设备配置信息
    logger.info("设备配置信息:")
    for device_type, device_id in DEVICE_IDS.items():
        status = "已配置" if device_id else "未配置"
        logger.info(f"  {device_type}: {device_id} ({status})")

    # 创建动态测试方法
    create_dynamic_tests()

    # 运行测试
    unittest.main(verbosity=2)